package org.xlp.expression;

import org.xlp.assertion.AssertUtils;
import org.xlp.fraction.BigFraction;

import java.math.BigDecimal;

/**
 * Description: 大实数加减乘除表达式解析器（包含括号）
 * <br/>date: 2024/12/26 19:25
 *
 * @version 1.0
 * @author: xlp
 */
public class BigDecimalAllExpressionParser implements IExpressionParser{
    private IExpressionItemParser itemParser;

    /**
     * 构造函数
     * @param expression 要解析的表达式
     * @throws NullPointerException 假如第参数为null或空，则抛出该异常
     */
    public BigDecimalAllExpressionParser(String expression){
       this.itemParser = new ExpressionItemParser(expression);
    }

    /**
     * 构造函数
     * @param itemParser 把表达式解析成计算项对的解析器
     * @throws NullPointerException 假如第参数为null，则抛出该异常
     */
    public BigDecimalAllExpressionParser(IExpressionItemParser itemParser){
        AssertUtils.isNotNull(itemParser, "itemParser parameter must be not null!");
        this.itemParser = itemParser;
    }

    /**
     * 解析函数
     *
     * @return 返回表达式结果
     * @throws ExpressionParseException 假如表达式不合法，则抛出该异常
     */
    @Override
    public Number parse() throws ExpressionParseException {
        //数据项解析，防止重复解析
        if (itemParser.getExpressionItems() == null){
            // 还未解析表达式项，则进行解析
            itemParser.parse();
        }
        StringArrayIterator iterator = new StringArrayIterator(itemParser.getExpressionItems());
        return calculateItem(iterator).getFraction();
    }

    private BigFraction calculateItem(StringArrayIterator iterator) {
        //计算乘除法
        BigFraction result = calculateItem2(iterator);

        while (iterator.hasNext()) {
            String next = iterator.next();
            switch (next) {
                case PLUS:
                    result = result.add(calculateItem2(iterator));
                    break;
                case MINUS:
                    result = result.subtract(calculateItem2(iterator));
                    break;
                case RIGHT_BRACKET:
                    return result;
                default:
                    result = getBigFractionFromDecimal(next);
            }
        }
        return result;
    }

    private BigFraction calculateItem2(StringArrayIterator iterator) {
        BigFraction result =  BigFraction.FRACTION_VALUE_OF_0;
        while (iterator.hasNext()) {
            String next = iterator.next();
            switch (next) {
                case MULTIPLY:
                    next = iterator.next();
                    if (next.equals(LEFT_BRACKET)){
                        result = result.multiply(calculateItem(iterator));
                    } else {
                        result = result.multiply(getBigFractionFromDecimal(next));
                    }
                    break;
                case DIVISION:
                    next = iterator.next();
                    if (next.equals(RIGHT_BRACKET)){
                        result = result.divide(calculateItem(iterator));
                    } else {
                        result = result.divide(getBigFractionFromDecimal(next));
                    }
                    break;
                case LEFT_BRACKET:
                    result = calculateItem(iterator);
                    break;
                case PLUS:
                case MINUS:
                case RIGHT_BRACKET:
                    iterator.previous();
                    return result;
                default:
                    result = getBigFractionFromDecimal(next);
            }
        }
        return result;
    }

    private BigFraction getBigFractionFromDecimal(String next) {
        BigFraction result;
        if (itemParser.hasE()) {
            result = BigFraction.valueOf(new BigDecimal(next));
        } else {
            result = BigFraction.fromDecimalStr(next);
        }
        return result;
    }
}
